using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Linq.Expressions;
using System.Data.Common;
using Agatha.Common.InversionOfControl;
using Microsoft.Data.Extensions;
using System.Data.SqlClient;

namespace EntityFramework.ServiceLayer
{
    //public interface IRepository { }

    //public interface IReadOnlyRepository<TEntity, TId> : IRepository
    //{
    //    /// <summary>
    //    /// Returns each entity of a certain type.
    //    /// </summary>
    //    /// <returns></returns>
    //    IEnumerable<TEntity> GetAll();

    //    /// <summary>
    //    /// Returns each entity of a certain type according to the given expression.
    //    /// </summary>
    //    /// <param name="expression"></param>
    //    /// <returns></returns>
    //    IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> expression);

    //    /// <summary>
    //    /// Returns a query which does a select all.
    //    /// </summary>
    //    /// <returns></returns>
    //    IQueryable<TEntity> QueryAll();

    //    /// <summary>
    //    /// Returns the entity for the given identifier, or null if no entity exists.
    //    /// </summary>
    //    /// <param name="id"></param>
    //    /// <returns></returns>
    //    TEntity Get(TId id);

    //    /// <summary>
    //    /// Counts each entity of a certain type.
    //    /// </summary>
    //    /// <returns></returns>
    //    int CountAll();

    //    /// <summary>
    //    /// Counts each entity of a certain type according to the given expression.
    //    /// </summary>
    //    /// <param name="expression"></param>
    //    /// <returns></returns>
    //    int Count(Expression<Func<TEntity, bool>> expression);

    //    /// <summary>
    //    /// Checks if an entity with the given identifier exists.
    //    /// Returns true if the entity exists, else false.
    //    /// </summary>
    //    /// <param name="id"></param>
    //    /// <returns></returns>
    //    bool Exists(TId id);

    //    /// <summary>
    //    /// Checks if the query returns a result.
    //    /// </summary>
    //    /// <param name="queryable"></param>
    //    /// <returns></returns>
    //    bool Exists(IQueryable<TEntity> queryable);

    //    /// <summary>
    //    /// Checks if an entity exists according to the given expression.
    //    /// </summary>
    //    /// <param name="expression"></param>
    //    /// <returns></returns>
    //    bool Exists(Expression<Func<TEntity, bool>> expression);
    //}

    public abstract class ReadOnlyRepository<TEntity, TId> 
        //: IReadOnlyRepository<TEntity, TId>
        where TEntity : EntityObject, IEntity<TId>
    {
        private readonly IActiveObjectContextManager activeObjectContextManager;

        protected IActiveObjectContextManager ActiveObjectContextManager
        {
            get { return activeObjectContextManager; }
        }
        protected ReadOnlyRepository()
            : this(IoC.Container.Resolve<IActiveObjectContextManager>())
        {
        }

        protected ReadOnlyRepository(IActiveObjectContextManager activeObjectContextManager)
        {
            this.activeObjectContextManager = activeObjectContextManager;
        }

        protected IObjectContext ObjectContext
        {
            get
            {
                return activeObjectContextManager.GetActiveObjectContext();
            }
        }

        public IQueryable<TEntity> QueryAll()
        {
            return ObjectContext.CreateQuery<TEntity>();
        }

        public IQueryable<T> QueryAll<T>()
        {
            return ObjectContext.CreateQuery<T>();
        }

        public IQueryable<TEntity> QueryAll(Expression<Func<TEntity, bool>> expression)
        {
            return QueryAll().Where(expression);
        }

        public IEnumerable<TEntity> GetAll()
        {
            return QueryAll().ToList();
        }

        public IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> expression)
        {
            return QueryAll().Where(expression).ToList();
        }

        public TEntity Get(TId id)
        {
            var entityKey = ObjectContext.CreateEntityKey<TEntity, TId>(id);

            try
            {
                return (TEntity) ObjectContext.GetObjectByKey(entityKey);
            } 
            catch (ObjectNotFoundException)
            {
                return null;
            }
        }

        public int CountAll()
        {
            return QueryAll().Count();
        }

        public int Count(Expression<Func<TEntity, bool>> expression)
        {
            return QueryAll().Where(expression).Count();
        }

        public bool Exists(TId id)
        {
            return Get(id) != null;
        }

        public bool Exists(IQueryable<TEntity> queryable)
        {
            return queryable.Count() > 0;
        }

        public bool Exists(Expression<Func<TEntity, bool>> expression)
        {
            return Exists(QueryAll().Where(expression));
        }

        protected DbCommand GetStoredProcedureCommand(string procedureName, IEnumerable<KeyValuePair<string, object>> parameters)
        {
            var command = ((ObjectContext)ObjectContext).CreateStoreCommand(procedureName, CommandType.StoredProcedure, parameters.Select(p => new SqlParameter(p.Key, p.Value ?? DBNull.Value)).ToArray());
            command.Transaction = ObjectContext.SqlTransaction();
            return command;
        }
    }
}